﻿

namespace Patterns4Net.PatternEnforcer.Core.Patterns
{
    using Mono.Cecil;
    using Patterns4Net.Core;
    using System.Linq;
    using Patterns4Net.Core.CecilExtensions;
    using Patterns4Net.Core.Patterns;

    [PatternCheckerExport(typeof(TemplateMethod))]
    public class TemplateMethodChecker : FluentPatternChecker<TemplateMethod>
    {
        private readonly PatternsLocator patternsLocator;

        public TemplateMethodChecker(PatternsLocator patternsLocator)
        {
            this.patternsLocator = patternsLocator;

            this.Type(pattern => pattern.TargetMethod.DeclaringType)
                .Check(
                    type => type.IsSealed == false,
                    (pattern, type) =>
                        string.Format(
                            ErrorMessages.TemplateMethodShouldBeInNonSealedType,
                            pattern.TargetMethod.Name,
                            type.Name));

            this.If(pattern => pattern.TargetMethod.DeclaringType.IsSealed == false)
                .Method(pattern => pattern.TargetMethod)
                .Check(
                    method => 
                        method.GetMethodCalls().Any(
                            call =>
                                call.TargetObject != null &&
                                call.TargetObject.IsThisParameter &&
                                (IsTemplateMethod(call.Method) ||
                                 call.Method.Resolve().IsOverriddeable())),
                    (pattern, method) => 
                        string.Format(
                            ErrorMessages.TemplateMethodDoesNotCallAnyOverrideableMethod,
                            method.Name,
                            method.DeclaringType.Name)
                );
        }

        public bool IsTemplateMethod(MethodReference method)
        {
            var resolved = method.Resolve();
            return resolved != null && 
                this.patternsLocator.GetPatternsOf(resolved).Any(pattern => pattern is TemplateMethod);
        }
    }
}
